/********************************************************************
	
	Tau Virtual Machine
	
	complex.c
	
	Copyright (c) 2009 Tau Virtual Machine Project
	See COPYING.txt for copying and redistribution conditions.
	
	Author:  Matt C.
	Date:  Jul 12, 2009
	
********************************************************************/

#include <stdlib.h>
#include "tau.h"

// GLOBALS **********************************************************

// ROUTINES *********************************************************

TauValue* TauMakeComplex (double real, double imag)
{
	TauValue* v;
	
	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_COMPLEX;
	v->refs = 0;

	v->data.c = (TauComplex*) malloc(sizeof(TauComplex));
	v->data.c->real = real;
	v->data.c->imag = imag;
	
	return v;
}

// negate *********************************************************************

TauValue* TauComplexNeg (TauValue* vs);
{
	return TauMakeComplex(-vs->data.c->real, -vs->data.c->imag);
}

// conjugate ******************************************************************

TauValue* TauComplexConj (TauValue* vs);
{
	return TauMakeComplex(vs->data.c->real, -vs->data.c->imag);
}

// add ************************************************************************

TauValue* TauComplexAdd (TauValue* vs, TauValue* vt);
{
	switch(vt->type)
	{
		case T_INTEGER:
			return TauMakeComplex(vs->data.c->real + vt->data.i, vs->data.c->imag);
		
		case T_REAL:
			return TauMakeComplex(vs->data.c->real + vt->data.d, vs->data.c->imag);
		
		case T_COMPLEX:
			return TauMakeComplex(vs->data.c->real + vt->data.c->real,
				vs->data.c->imag + vt->data.c->imag);
		
		default:
			printf("Error:\n");
			exit(1);
	}
}

// subtract *******************************************************************

TauValue* TauComplexSub (TauValue* vs, TauValue* vt);
{
	switch(vt->type)
	{
		case T_INTEGER:
			return TauMakeComplex(vs->data.c->real - vt->data.i, vs->data.c->imag);
		
		case T_REAL:
			return TauMakeComplex(vs->data.c->real - vt->data.d, vs->data.c->imag);
		
		case T_COMPLEX:
			return TauMakeComplex(vs->data.c->real - vt->data.c->real,
				vs->data.c->imag - vt->data.c->imag);
		
		default:
			printf("Error:\n");
			exit(1);
	}
}

// multiply *******************************************************************

OpalObject* OpalComplex_multiply (OpalObject* x, OpalObject* y)
{
	double a, b, c, d;
	double r, i;
	
	switch(TYPE(y))
	{
		case T_INTEGER:
			return OpalComplex_new(	O_COMPLEX(x)->real * O_INTEGER(y)->value,
									O_COMPLEX(x)->imag * O_INTEGER(y)->value);
		
		case T_REAL:
			return OpalComplex_new(	O_COMPLEX(x)->real * O_REAL(y)->value,
									O_COMPLEX(x)->imag * O_REAL(y)->value);
		
		case T_COMPLEX:
			// (a + jb)(c + jd)
			
			a = O_COMPLEX(x)->real;
			b = O_COMPLEX(x)->imag;
			c = O_COMPLEX(y)->real;
			d = O_COMPLEX(y)->imag;
			
			r = a*c - b*d;
			i = a*d + b*c;

			return OpalComplex_new(r, i);
		
		default:
			printf("Error:\n");
			exit(1);
	}
}

// divide *********************************************************************

OpalObject* OpalComplex_divide (OpalObject* x, OpalObject* y)
{
	double a, b, c, d;
	double pr, pi, q;

	switch(TYPE(y))
	{
		case T_INTEGER:
			return OpalComplex_new(	O_COMPLEX(x)->real / O_INTEGER(y)->value,
									O_COMPLEX(x)->imag / O_INTEGER(y)->value);
		
		case T_REAL:
			return OpalComplex_new(	O_COMPLEX(x)->real / O_REAL(y)->value,
									O_COMPLEX(x)->imag / O_REAL(y)->value);
		
		case T_COMPLEX:
			// (a + jb)(c + jd)
			
			a = O_COMPLEX(x)->real;
			b = O_COMPLEX(x)->imag;
			c = O_COMPLEX(y)->real;
			d = O_COMPLEX(y)->imag;
			
			pr = b*c + a*d;
			pi = a*b - c*d;
			q  = b*b + d;
			
			return OpalComplex_new(pr/q, pi/q);
			
		default:
			printf("Error:\n");
			exit(1);
	}
}

// not ************************************************************************


OpalObject* OpalComplex_not (OpalObject* x)
{
	return OpalInteger_new( !(O_COMPLEX(x)->real || O_COMPLEX(x)->imag) );
}

// equal **********************************************************************

OpalObject* OpalComplex_equal (OpalObject* x, OpalObject* y)
{
	int t1, t2;

	switch(TYPE(y))
	{
		case T_INTEGER:
			t1 = O_COMPLEX(x)->real == O_INTEGER(y)->value;
			t2 = O_COMPLEX(x)->imag == 0.0;
			return OpalInteger_new(t1 && t2);
		
		case T_REAL:
			t1 = O_COMPLEX(x)->real == O_REAL(y)->value;
			t2 = O_COMPLEX(x)->imag == 0.0;
			return OpalInteger_new(t1 && t2);
		
		case T_COMPLEX:
			t1 = O_COMPLEX(x)->real == O_COMPLEX(y)->real;
			t2 = O_COMPLEX(x)->imag == O_COMPLEX(y)->imag;
			return OpalInteger_new(t1 && t2);
		
		default:
			printf("Error:\n");
			exit(1);
	}
}

// not equal ******************************************************************

OpalObject* OpalComplex_notEqual (OpalObject* x, OpalObject* y)
{
	int t1, t2;

	switch(TYPE(y))
	{
		case T_INTEGER:
			t1 = O_COMPLEX(x)->real != O_INTEGER(y)->value;
			t2 = O_COMPLEX(x)->imag != 0.0;
			return OpalInteger_new(t1 || t2);
		
		case T_REAL:
			t1 = O_COMPLEX(x)->real != O_REAL(y)->value;
			t2 = O_COMPLEX(x)->imag != 0.0;
			return OpalInteger_new(t1 || t2);
		
		case T_COMPLEX:
			t1 = O_COMPLEX(x)->real != O_COMPLEX(y)->real;
			t2 = O_COMPLEX(x)->imag != O_COMPLEX(y)->imag;
			return OpalInteger_new(t1 || t2);
		
		default:
			printf("Error:\n");
			exit(1);
	}
}

// greater ********************************************************************

OpalObject* OpalComplex_greater (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// less ***********************************************************************

OpalObject* OpalComplex_less (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// greater equal **************************************************************

OpalObject* OpalComplex_greaterEqual (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// less equal *****************************************************************

OpalObject* OpalComplex_lessEqual (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// bit not ********************************************************************

OpalObject* OpalComplex_bitNot (OpalObject* x)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// bit and ********************************************************************

OpalObject* OpalComplex_bitAnd (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// bit or *********************************************************************

OpalObject* OpalComplex_bitOr (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// bit xor ********************************************************************

OpalObject* OpalComplex_bitXor (OpalObject* x, OpalObject* y)
{
	printf("Error:  Undefined Operation.\n");
	exit(1);
}

// END ************************************************************************



